# Copyright (C) 2005-2006 Frederic Back <fredericback@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

import gtk
import cairo

class SymbolRenderer:
    """ A symbol drawing engine.

    Parses 'path data' and draws on a cairo context. SymbolRenderer is used by
    the Layer class to store symbol data.

    Usage: Pass a string of the form "operation(param,...) ..."
    Valid drawing operations are:

    circle(x,y,r)
    rectangle(x,y,width,height)
    move_to(x,y)
    rel_move_to(x,y)
    line_to(x,y)
    rel_line_to(x,y)

    fill()
    fill_preserve()
    stroke()
    stroke_preserve()

    set_source_rgba(r,g,b,a)
    set_line_colour()
    set_ouline_colour()
    set_fill_colour()
    """

    commonSymbols = {
        "cross": "move_to(-5,-5) line_to(5,5) move_to(-5,5) line_to(5,-5) set_outline_colour() stroke()",

        "triangle 1": "move_to(-5,4) line_to(0,-4) line_to(5,4) line_to(-5,4) set_outline_colour() stroke()",
        "triangle 2": "move_to(-5,4) line_to(0,-4) line_to(5,4) line_to(-5,4)  set_fill_colour() fill_preserve() set_outline_colour() stroke()",
        "triangle 3": "move_to(-5,-4) line_to(0,4) line_to(5,-4) line_to(-5,-4) set_outline_colour() stroke()",
        "triangle 4": "move_to(-5,-4) line_to(0,4) line_to(5,-4) line_to(-5,-4)  set_fill_colour() fill_preserve() set_outline_colour() stroke()",

        "circle 1": "circle(0,0,5) set_fill_colour() fill()",
        "circle 2": "circle(0,0,5) set_outline_colour() stroke()",
        "circle 3": "circle(0,0,5) set_fill_colour() fill_preserve() set_outline_colour() stroke()",

        "rectangle 1": "rectangle(-5,-5,10,10) set_fill_colour() fill()",
        "rectangle 1": "rectangle(-5,-5,10,10) set_outline_colour() stroke()",
        "rectangle 1": "rectangle(-5,-5,10,10) set_fill_colour() fill_preserve() set_outline_colour() stroke()",

        "double rectangle": "rectangle(-5,-5,10,10) set_fill_colour() fill_preserve() set_outline_colour() stroke() \
            rectangle(-2,-2,4,4) set_outline_colour() fill() ",

        "funky": "circle(0,0,5) set_line_width(2) set_line_colour() stroke() #outer_circle \
            circle(0,0,3) #inner_circle \
            set_fill_colour() fill_preserve() \
            set_outline_colour() set_line_width(1) stroke()"
        }
        
    def __init__(self):
        self.operationString = ""
        self.__path = ""
        self.scale = 1.0

    def render(self, ctx ):
        ctx.save()
        ctx.scale(self.scale,self.scale)
        exec(self.operationString)
        ctx.restore()

    def renderToPixbuf(self, width, height):
        pixmap = gtk.gdk.Pixmap(None,width,height,24)
        ctx = pixmap.cairo_create()
        ctx.rectangle(0,0,width,height)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()
        ctx.translate(width/2,height/2)
        self.render(ctx)
        pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,width,height)
        pixbuf.get_from_drawable(pixmap,pixmap.get_colormap(),0,0,0,0,width, height)
        return pixbuf

    def getPath(self):
        return self.__path

    def recreate(self, line, outline, fill, pathString, scale ):
        """ parse a string containing path and stroke operations for cairos. 
            The string should be like: "circle(0,0,10) stroke(outlineColour)"

            Note: Parsing like this is not really optimal, but safer than simply
            executing or evaluating an expression.
        """

        self.line = line.asCairoRGBA()
        self.outline = outline.asCairoRGBA()
        self.fill = fill.asCairoRGBA()
        self.scale = float(scale)
        self.__path = pathString

        #------------------------------------ the following operations are valid
        def circle(x,y,r):
            op.append( "ctx.new_path()" )
            op.append( "ctx.arc(%f,%f,%f,0,6.2831853071795862)"%(x,y,r) )
        def rectangle(x,y,width,height):
            op.append( "ctx.rectangle(%f,%f,%f,%f)"%(x,y,width,height) )

        def move_to(x,y):
            op.append( "ctx.move_to(%f,%f)"%(x,y) )
        def rel_move_to(x,y):
            op.append( "ctx.rel_move_to(%f,%f)"%(x,y) )
        def line_to(x,y):
            op.append( "ctx.line_to(%f,%f)"%(x,y) )
        def rel_line_to(x,y):
            op.append( "ctx.rel_line_to(%f,%f)"%(x,y) )

        def fill():
            op.append( "ctx.fill()" )
        def fill_preserve():
            op.append( "ctx.fill_preserve()" )
        def stroke():
            op.append( "ctx.stroke()" )
        def stroke_preserve():
            op.append( "ctx.stroke_preserve()" )
        def set_line_width( width ):
            op.append( "ctx.set_line_width(%f)"%width )

        def set_source_rgba(r,g,b,a):
            op.append( "ctx.set_source_rgba(%f,%f,%f,%f)"%(r,g,b,a) )
        def set_line_colour():
            set_source_rgba(*self.line)
        def set_ouline_colour():
            set_source_rgba(*self.outline)
        def set_fill_colour():
            set_source_rgba(*self.fill)

        #------------------------------------------------------------ parse path
        op = []
        path = pathString.split()
        def next():return float(path.pop(0))
        move_to(0,0)
        set_line_width(1)
        while len(path) > 0:
            token = path.pop(0)
            if token[0] == "#": continue
            try: exec(token) in {
                    "circle": circle, "rectangle": rectangle,
                    "move_to": move_to, "rel_move_to": rel_move_to,
                    "line_to": line_to, "rel_line_to": rel_line_to,
                    "set_source_rgba": set_source_rgba,
                    "set_line_colour": set_line_colour,
                    "set_line_width": set_line_width,
                    "set_outline_colour": set_ouline_colour,
                    "set_fill_colour": set_fill_colour,
                    "fill": fill, "fill_preserve": fill_preserve, 
                    "stroke": stroke, "stroke_preserve": stroke_preserve
                    }, {}
            except Exception, e:
                print "could not parse '%s' in '%s'.\n%s"%(token,pathString,e)
                print "proceeding happily..."

                # use a black dot instead
                op = ["ctx.move_to(0,0)"]
                circle(0,0,7)
                set_source_rgba(0,0,0,1)
                stroke()

        self.operationString = ""
        for command in op:
            self.operationString += command + "\n"

